#ifndef GML__TEMPLATE_H
#define GML__TEMPLATE_H

#if defined(randomAccessIterators)
#if defined(bidirectionalIterators)
#define randomAccessIteratorsWith(_Operate) randomAccessIterators(_Operate),
#else
#define randomAccessIteratorsWith(_Operate) randomAccessIterators(_Operate)
#endif
#else
#define randomAccessIteratorsWith(_Operate)
#define randomAccessIterators(_Operate)
#endif

#if defined(bidirectionalIterators)
#if defined(forwardIterators)
#define bidirectionalIteratorsWith(_Operate) bidirectionalIterators(_Operate),
#else
#define bidirectionalIteratorsWith(_Operate) bidirectionalIterators(_Operate)
#endif
#else
#define bidirectionalIteratorsWith(_Operate)
#define bidirectionalIterators(_Operate)
#endif

#if defined(forwardIterators)
#if defined(outputIterators)
#define forwardIteratorsWith(_Operate) forwardIterators(_Operate),
#else
#define forwardIteratorsWith(_Operate) forwardIterators(_Operate)
#endif
#else
#define forwardIteratorsWith(_Operate)
#define forwardIterators(_Operate)
#endif

#ifndef outputIterators
#define outputIterators(_Operate)
#endif

#define iteratorCategory(_Category, _iterator) ({ \
    _Generic((*_iterator), \
        randomAccessIteratorsWith(_Operate) \
        bidirectionalIteratorsWith(_Operate) \
        forwardIteratorsWith(_Operate) \
        outputIterators(_Operate) \
    )(); \
})

#define iteratorRandomAccessOperate(_Operate, _this, ...) ({ \
    _Generic((*_this), \
        randomAccessIterators(_Operate) \
    )(_this, ##__VA_ARGS__); \
})

#define iteratorBidirectionalOperate(_Operate, _this, ...) ({ \
    _Generic((*_this), \
        randomAccessIteratorsWith(_Operate) \
        bidirectionalIterators(_Operate) \
    )(_this, ##__VA_ARGS__); \
})

#define iteratorForwardOperate(_Operate, _this, ...) ({ \
    _Generic((*_this), \
        randomAccessIteratorsWith(_Operate) \
        bidirectionalIteratorsWith(_Operate) \
        forwardIterators(_Operate) \
    )(_this, ##__VA_ARGS__); \
})

#define iteratorOutputOperate(_Operate, _this, ...) ({ \
    _Generic((*_this), \
        randomAccessIteratorsWith(_Operate) \
        bidirectionalIteratorsWith(_Operate) \
        forwardIteratorsWith(_Operate) \
        outputIterators(_Operate) \
    )(_this, ##__VA_ARGS__); \
})

#define iteratorEqualTo(_this, _iterator) ({ \
    iteratorOutputOperate(EqualTo, _this, _iterator); \
})

#define iteratorNotEqualTo(_this, _iterator) ({ \
    iteratorOutputOperate(NotEqualTo, _this, _iterator); \
})

#define iteratorLessThan(_this, _iterator) ({ \
    iteratorRandomAccessOperate(LessThan, _this, _iterator); \
})

#define iteratorIndirect(_this) ({ \
    iteratorOutputOperate(Indirect, _this); \
})

#define iteratorSubscript(_this, _n) ({ \
    iteratorRandomAccessOperate(Subscript, _this, _n); \
})

#define iteratorIncreasePrefix(_this) ({ \
    iteratorForwardOperate(IncreasePrefix, _this); \
})

#define iteratorIncreasePostfix(_this) ({ \
    iteratorForwardOperate(IncreasePostfix, _this); \
})

#define iteratorDecreasePrefix(_this) ({ \
    iteratorBidirectionalOperate(DecreasePrefix, _this); \
})

#define iteratorDecreasePostfix(_this) ({ \
    iteratorBidirectionalOperate(DecreasePostfix, _this); \
})

#endif // GML__TEMPLATE_H
